{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "143aa85b",
   "metadata": {},
   "source": [
    "| [01_base/10_文件和异常.ipynb](https://github.com/shibing624/python-tutorial/blob/master/01_base/10_文件和异常.ipynb)  | Python文件和异常  |[Open In Colab](https://colab.research.google.com/github/shibing624/python-tutorial/blob/master/01_base/10_文件和异常.ipynb) |\n",
    "\n",
    "# 文件\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1032224b",
   "metadata": {},
   "source": [
    "## 写文件\n",
    "我们使用 open 函数的写入模式来写文件："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "707cd83f",
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('test.txt', 'w')\n",
    "f.write('hello world.')\n",
    "f.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "60a007cc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello world.\n"
     ]
    }
   ],
   "source": [
    "print(open('test.txt').read())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1a6f6624",
   "metadata": {},
   "source": [
    "使用 w 模式时，如果文件不存在会被创建\n",
    "\n",
    "除了写入模式，还有追加模式 a\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "227b6e3d",
   "metadata": {},
   "source": [
    "读写模式w+"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "99eff484",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lo world. morning.\n"
     ]
    }
   ],
   "source": [
    "f = open('test.txt', 'w+')\n",
    "f.write('hello world. morning.')\n",
    "f.seek(3)\n",
    "print(f.read())  # hello world.\n",
    "f.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8d2cecaa",
   "metadata": {},
   "source": [
    "\n",
    "## 读文件\n",
    "使用 open 函数 来读文件，使用文件名的字符串作为输入参数：\n",
    "\n",
    "默认打开文件是 ‘r’ 读模式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "57d58919",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello world. morning.\n"
     ]
    }
   ],
   "source": [
    "f = open(\"test.txt\")\n",
    "\n",
    "# 默认以读的方式打开文件，如果文件不存在会报错。\n",
    "# 可以使用 read 方法来读入文件中的所有内容：\n",
    "text = f.read()\n",
    "print(text)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c8254f82",
   "metadata": {},
   "source": [
    "按照行读入内容，readlines 方法返回一个列表，每个元素代表文件中每一行的内容："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "a99d0dfa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['hello world. morning.']\n"
     ]
    }
   ],
   "source": [
    "f = open(\"test.txt\")\n",
    "lines = f.readlines()\n",
    "print(lines)\n",
    "f.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "8d88f1ad",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello world. morning.\n"
     ]
    }
   ],
   "source": [
    "# 事实上，我们可以将 f 放在一个循环中，得到它每一行的内容：\n",
    "f = open('test.txt')\n",
    "for line in f:\n",
    "    print(line)\n",
    "f.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1f768868",
   "metadata": {},
   "source": [
    "## 二进制文件\n",
    "\n",
    "二进制读写模式 b："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "1a1fe6bb",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "f = open('binary.bin', 'wb')\n",
    "f.write(os.urandom(10))\n",
    "f.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "f0a4d21a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'\\xcd9\\x1c\\x91\\xe4\\x0fjZ\\xb7W'\n"
     ]
    }
   ],
   "source": [
    "f = open('binary.bin', 'rb')\n",
    "print(repr(f.read()))\n",
    "f.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc49f6e1",
   "metadata": {},
   "source": [
    "## with 方法\n",
    "事实上，Python提供了更安全的方法，当 with 块的内容结束后，\n",
    "Python会自动调用它的close 方法，确保读写的安全："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "30f1b00f",
   "metadata": {},
   "outputs": [
    {
     "ename": "ZeroDivisionError",
     "evalue": "float division by zero",
     "output_type": "error",
     "traceback": [
      "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[0;31mZeroDivisionError\u001B[0m                         Traceback (most recent call last)",
      "\u001B[0;32m<ipython-input-49-e1a25e505720>\u001B[0m in \u001B[0;36m<module>\u001B[0;34m\u001B[0m\n\u001B[1;32m      1\u001B[0m \u001B[0;32mwith\u001B[0m \u001B[0mopen\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m'new_file.txt'\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m'w'\u001B[0m\u001B[0;34m)\u001B[0m \u001B[0;32mas\u001B[0m \u001B[0mf\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m      2\u001B[0m     \u001B[0;32mfor\u001B[0m \u001B[0mi\u001B[0m \u001B[0;32min\u001B[0m \u001B[0mrange\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;36m3000\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m----> 3\u001B[0;31m         \u001B[0mx\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0;36m1.0\u001B[0m \u001B[0;34m/\u001B[0m \u001B[0;34m(\u001B[0m\u001B[0mi\u001B[0m \u001B[0;34m-\u001B[0m \u001B[0;36m1000\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m      4\u001B[0m         \u001B[0mf\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mwrite\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m'hello world: '\u001B[0m \u001B[0;34m+\u001B[0m \u001B[0mstr\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mi\u001B[0m\u001B[0;34m)\u001B[0m \u001B[0;34m+\u001B[0m \u001B[0;34m'\\n'\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;31mZeroDivisionError\u001B[0m: float division by zero"
     ]
    }
   ],
   "source": [
    "with open('new_file.txt', 'w') as f:\n",
    "    for i in range(3000):\n",
    "        x = 1.0 / (i - 1000)\n",
    "        f.write('hello world: ' + str(i) + '\\n')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c12cbe65",
   "metadata": {},
   "source": [
    "与 try/exception/finally 效果相同，但更简单。\n",
    "\n",
    "查看文件写的结果，虽然触发error，但已经写的内容是成功的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "018a36df",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello world: 990\r\n",
      "hello world: 991\r\n",
      "hello world: 992\r\n",
      "hello world: 993\r\n",
      "hello world: 994\r\n",
      "hello world: 995\r\n",
      "hello world: 996\r\n",
      "hello world: 997\r\n",
      "hello world: 998\r\n",
      "hello world: 999\r\n"
     ]
    }
   ],
   "source": [
    "!tail new_file.txt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "231fa3ec",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "    1000 new_file.txt\r\n"
     ]
    }
   ],
   "source": [
    "!wc -l new_file.txt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "6b513564",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 删除文件：\n",
    "import os\n",
    "os.remove('test.txt')\n",
    "os.remove('binary.bin')\n",
    "os.remove('new_file.txt')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f75d97d9",
   "metadata": {},
   "source": [
    "# 异常\n",
    "\n",
    "## try & except 块\n",
    "\n",
    "捕捉不同的错误类型，尝试在下面输入框输入：-1，1，2，q"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "id": "48a1ec3e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      ">-1\n",
      "value must bigger than 0\n",
      ">1\n",
      "the value must not be 1\n",
      ">2\n",
      "1/log10(2.0) = 3.321928094887362\n",
      ">q\n"
     ]
    }
   ],
   "source": [
    "import math\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        text = input('>')\n",
    "        if text[0] == 'q':\n",
    "            break\n",
    "        x = float(text)\n",
    "        y = 1 / math.log10(x)\n",
    "        print(\"1/log10({0}) = {1}\".format(x, y))\n",
    "    except ValueError:\n",
    "        print(\"value must bigger than 0\")\n",
    "    except ZeroDivisionError:\n",
    "        print(\"the value must not be 1\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dfc422e5",
   "metadata": {},
   "source": [
    "## 自定义异常\n",
    "异常是标准库中的类，这意味着我们可以自定义异常类：\n",
    "\n",
    "尝试在文本输入框输入：k，start，q"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "id": "78825e67",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "bad command operation. must input 'start', 'stop', 'pause'\n",
      ">k\n",
      "bad command string: k\n",
      ">start\n",
      "input command: start\n",
      ">q\n"
     ]
    }
   ],
   "source": [
    "class CommandError(ValueError):\n",
    "    print(\"bad command operation. must input 'start', 'stop', 'pause'\")\n",
    "    \n",
    "\n",
    "\n",
    "valid_commands = {'start', 'stop', 'pause'}\n",
    "while True:\n",
    "    command = input('>')\n",
    "    if command == 'q':\n",
    "        break\n",
    "    try:\n",
    "        if command.lower() not in valid_commands:\n",
    "            raise CommandError('Invalid command: %s' % command)\n",
    "        print('input command:', command)\n",
    "    except CommandError:\n",
    "        print(\"bad command string: %s\" % command)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5cf54db9",
   "metadata": {},
   "source": [
    "## finally\n",
    "try/catch 块还有一个可选的关键词 finally。\n",
    "\n",
    "不管 try 块有没有异常， finally 块的内容总是会被执行，\n",
    "而且会在抛出异常前执行，因此可以用来作为安全保证，\n",
    "\n",
    "比如文件操作时，常在finally关闭文件。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "id": "4e9af2a6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "divide by 0.\n",
      "finally was called.\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    print(1 / 0)\n",
    "except ZeroDivisionError:\n",
    "    print('divide by 0.')\n",
    "finally:\n",
    "    print('finally was called.')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "814bca7b",
   "metadata": {},
   "source": [
    "本节完。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}